import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import axios from 'axios'
import FileSaver from 'file-saver'
import { AxiosLoading } from './loading'
import { blobValidate, tansParams } from '@/utils/ruoyi'
import { STORAGE_AUTHORIZE_KEY, STORAGE_CLIENTID_KEY, useAuthorization } from '~/composables/authorization'
import { ContentTypeEnum, RequestEnum } from '~#/http-enum'
import { errorCode } from '@/utils/errorCode'
import { HttpStatus } from '@/enums/RespEnum'
import cache from '@/plugins/cache'
import { decrypt, encrypt } from '@/utils/jsencrypt'
import { decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto'

export const isRelogin = { show: false }
const encryptHeader = 'encrypt-key'

export function globalHeaders() {
  const token = useAuthorization()
  return {
    Authorization: `Bearer ${token.value}`,
    clientid: import.meta.env.VITE_APP_CLIENT_ID,
  }
}

export interface ResponseBody<T = any> {
  code: number
  data?: T
  msg: string
}

export interface RequestConfigExtra {
  token?: boolean
  customDev?: boolean
  loading?: boolean
}
const instance: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API ?? '/',
  timeout: 60000,
  headers: { 'Content-Type': ContentTypeEnum.JSON },
})
const axiosLoading = new AxiosLoading()
async function requestHandler(config: InternalAxiosRequestConfig & RequestConfigExtra): Promise<InternalAxiosRequestConfig> {
  // 处理请求前的url
  if (
    import.meta.env.DEV
      && import.meta.env.VITE_APP_BASE_API
      && import.meta.env.VITE_APP_BASE_URL_DEV
      && config.customDev
  ) {
    //  替换url的请求前缀baseUrl
    config.baseURL = import.meta.env.VITE_APP_BASE_API
  }
  const token = useAuthorization()

  if (token.value && config.token !== false)
    config.headers.set(STORAGE_AUTHORIZE_KEY, `Bearer ${token.value}`)

  config.headers.set(STORAGE_CLIENTID_KEY, import.meta.env.VITE_APP_CLIENT_ID)

  // 增加多语言的配置
  // const { locale } = useI18nLocale()
  config.headers.set('Content-Language', 'zh_CN')
  const { selectMansionId } = useUserStore()
  config.headers.set('projectId', selectMansionId)
  if (config.loading)
    axiosLoading.addLoading()

  // get请求映射params参数
  if (config.method === 'get' && config.params) {
    let url = `${config.url}?${tansParams(config.params)}`
    url = url.slice(0, -1)
    config.params = {}
    config.url = url
  }
  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
  const isEncrypt = (config.headers || {}).isEncrypt === 'false'
  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime(),
    }
    const sessionObj = cache.session.getJSON('sessionObj')
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj)
    }
    else {
      const s_url = sessionObj.url // 请求地址
      const s_data = sessionObj.data // 请求数据
      const s_time = sessionObj.time // 请求时间
      const interval = 500 // 间隔时间(ms)，小于此时间视为重复提交
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '操作太快了，请稍后再试!'
        console.warn(`[${s_url}]: ${message}`)
        return Promise.reject(new Error(message))
      }
      else {
        cache.session.setJSON('sessionObj', requestObj)
      }
    }
  }
  // 当开启参数加密
  if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
    // 生成一个 AES 密钥
    const aesKey = generateAesKey()
    config.headers[encryptHeader] = encrypt(encryptBase64(aesKey))
    config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey)
  }
  // FormData数据去请求头Content-Type
  if (config.data instanceof FormData)
    delete config.headers['Content-Type']

  return config
}

function responseHandler(res: any): ResponseBody<any> | AxiosResponse<any> | Promise<any> | any {
  const AntMessage = useMessage()
  const AntModal = useModal()
  const keyStr = res.headers[encryptHeader]
  // 加密
  if (keyStr != null && keyStr !== '') {
    const data = res.data
    // 请求体 AES 解密
    const base64Str = decrypt(keyStr)
    // base64 解码 得到请求头的 AES 秘钥
    const aesKey = decryptBase64(base64Str.toString())
    // aesKey 解码 data
    const decryptData = decryptWithAes(data, aesKey)
    // 将结果 (得到的是 JSON 字符串) 转为 JSON
    res.data = JSON.parse(decryptData)
  }
  // 未设置状态码则默认成功状态
  const code = res.data.code || HttpStatus.SUCCESS
  // 获取错误信息
  const msg = errorCode[code] || res.data.msg || errorCode.default
  // 二进制数据则直接返回
  if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer')
    return res.data

  if (code === 401) {
    // prettier-ignore
    if (!isRelogin.show) {
      isRelogin.show = true
      AntModal.confirm({
        title: '登录状态已过期，您可以继续留在该页面，或者重新登录',
        content: '',
        onOk() {
          isRelogin.show = false
          useUserStore().logout().then(() => {
            location.href = `/index`
          })
        },
        onCancel() {
          isRelogin.show = false
        },
      })
    }
    // eslint-disable-next-line prefer-promise-reject-errors
    return Promise.reject('无效的会话，或者会话已过期，请重新登录。')
  }
  else if (code === HttpStatus.SERVER_ERROR) {
    console.log(msg)
    AntMessage.error(msg)
    return Promise.reject(new Error(msg))
  }
  else if (code === HttpStatus.WARN) {
    AntMessage.warning(msg)
    return Promise.reject(new Error(msg))
  }
  else if (code !== HttpStatus.SUCCESS) {
    AntMessage.error(msg)
    // eslint-disable-next-line prefer-promise-reject-errors
    return Promise.reject('error')
  }
  else {
    return Promise.resolve(res.data)
  }
}

function errorHandler(error: AxiosError): Promise<any> {
  const AntMessage = useMessage()
  let { message } = error
  if (message === 'Network Error')
    message = '后端接口连接异常'
  else if (message.includes('timeout'))
    message = '系统接口请求超时'
  else if (message.includes('Request failed with status code'))
    message = `系统接口${message.substr(message.length - 3)}异常`

  try {
    AntMessage.error(message, 5)
  }
  catch (e) {}
  return Promise.reject(error)
}
interface AxiosOptions<T> {
  url: string
  params?: T
  data?: T
}
instance.interceptors.request.use(requestHandler)

instance.interceptors.response.use(responseHandler, errorHandler)

export default instance
function instancePromise<R = any, T = any>(options: AxiosOptions<T> & RequestConfigExtra): Promise<ResponseBody<R>> {
  const { loading } = options
  return new Promise((resolve, reject) => {
    instance.request(options).then((res) => {
      resolve(res as any)
    }).catch((e: Error | AxiosError) => {
      reject(e)
    })
      .finally(() => {
        if (loading)
          axiosLoading.closeLoading()
      })
  })
}
export function useGet< R = any, T = any>(url: string, params?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
  const options = {
    url,
    params,
    method: RequestEnum.GET,
    ...config,
  }
  return instancePromise< R, T >(options)
}

export function usePost< R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
  const options = {
    url,
    data,
    method: RequestEnum.POST,
    ...config,
  }
  return instancePromise< R, T >(options)
}

export function usePut< R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
  const options = {
    url,
    data,
    method: RequestEnum.PUT,
    ...config,
  }
  return instancePromise<R, T>(options)
}

export function useDelete< R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
  const options = {
    url,
    data,
    method: RequestEnum.DELETE,
    ...config,
  }
  return instancePromise<R, T>(options)
}
// 通用下载方法
export function download(url: string, params: any, fileName: string) {
  const AntMessage = useMessage()
  const AntLoading = useLoading({ text: '正在下载...' })
  AntLoading.open()
  // prettier-ignore
  return instance.post(url, params, {
    transformRequest: [
      (params: any) => {
        return tansParams(params)
      },
    ],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob',
  }).then(async (resp: any) => {
    const isLogin = blobValidate(resp)
    if (isLogin) {
      const blob = new Blob([resp])
      FileSaver.saveAs(blob, fileName)
    }
    else {
      const resText = await resp.data.text()
      const rspObj = JSON.parse(resText)
      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode.default

      AntMessage.error(errMsg)
    }
    AntLoading.close()
  }).catch((r: any) => {
    console.error(r)
    AntMessage.error('下载文件出现错误，请联系管理员！')
    AntLoading.close()
  })
}
